import { watch, ref, reactive } from "vue";
import type { Ref } from "vue";
import type {
  DictManager,
  Dict,
  DictLoader,
  DictInjectOptions,
} from "@/views/table/types";

class DM implements DictManager {
  _container: any = {};
  _map: any = {};

  #watchReference(key: string) {
    watch(this.extract(key), (v) => {
      console.log(v);
      const sync = (v: Dict[]) => {
        for (const it of v) {
          this._map[key][it.value] = it;
        }
      };
      sync(v);
    });
  }

  /**
   * 抽取已经缓存的内容
   * @param key 保存的键
   * @param condition 过滤条件
   * @param failCount 递归 fallback
   */
  extract(
    key: string,
    condition?: (it: Dict) => boolean,
    failCount = 0
  ): Ref<Dict[]> {
    if (!this._container[key]) {
      this._container[key] = { data: ref([]) };
      this.#watchReference(key);
      return this._container[key].data;
    }
    if (failCount > 10) {
      return ref([]);
    }
    const data = this._container[key]?.data;
    if (!data) {
      return this.extract(key, condition, ++failCount);
    }
    if (condition) {
      return ref(data.value.filter(condition));
    }
    return data;
  }

  /**
   * 以对象的形式抽取出缓存内容
   * @param key 保存的键
   */
  map(key: string): any {
    if (!this._map[key]) {
      this._map[key] = reactive({});
    }
    return this._map[key];
  }

  /**
   * 注入内容
   * @param key  保存的键
   * @param loader 数据加载器
   * @param options 配置项(TODO next version)
   */
  // eslint-disable-next-line
  inject<T>(key: string, loader: DictLoader<T>, options?: DictInjectOptions): DictManager {
    if (!this._container[key]) {
      this._container[key] = {
        data: ref([]),
        loader,
        interval: 0,
        createAt: new Date().getTime(),
      };
      this.#watchReference(key);
    } else {
      this._container[key].loader = loader;
    }

    loader.onLoad().then((response) => {
      this._container[key].data.value = loader.onMapping(response);
      this.map(key);
    });
    return this;
  }

  /**
   * 重构当前字典结构，
   * 该操作会刷新当前字典内的所有方式，其原理为将所有的 loader 全部重新调用一次
   * 实现上依赖 {@link reload} 以该函数的具体实现为准
   */
  refactor(): void {
    for (const key of Object.keys(this._container)) {
      this.reload(key);
    }
  }

  /**
   * 刷新单一 key 下的数据
   * @param key 保存的键
   */
  reload(key: string): void {
    const cache = this._container[key];
    if (cache) {
      this.inject(key, cache.loader);
    }
  }
}
const $dm = new DM();

export const useDictManager = () => $dm;
